{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "(model-serve-get-started)=\n",
    "# Getting started\n",
    "\n",
    "This simple serving graph example uses a custom class and a custom function. See [custom steps](./writing-custom-steps.ipynb) for more details.\n",
    "\n",
    "**In this section**\n",
    "- [Steps](#steps)\n",
    "- [Create a project and function](#create-a-project-and-function)\n",
    "- [Build the graph](#build-the-graph)\n",
    "- [Visualize the graph](#visualize-the-graph)\n",
    "- [Test the function on a mock server](#test-the-function-on a-mock-server)\n",
    "- [Deploy the function](#deploy-the-function)\n",
    "- [Test the deployed function](#test-the-deployed-function)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Steps\n",
    "\n",
    "The following code defines basic steps that illustrate building a graph. These steps are:\n",
    "\n",
    "- **`inc`**: increments the value by 1.\n",
    "- **`mul`**: multiplies the value by 2.\n",
    "- **`WithState`**: class that increments an internal counter, prints an output, and adds the input value to the current counter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile serving-graph.py\n",
    "\n",
    "def inc(x):\n",
    "    return x + 1\n",
    "\n",
    "\n",
    "def mul(x):\n",
    "    return x * 2\n",
    "\n",
    "class WithState:\n",
    "    def __init__(self, name, context, init_val=0):\n",
    "        self.name = name\n",
    "        self.context = context\n",
    "        self.counter = init_val\n",
    "\n",
    "    def do(self, x):\n",
    "        self.counter += 1\n",
    "        print(f\"Echo: {self.name}, x: {x}, counter: {self.counter}\")\n",
    "        return x + self.counter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a project and function\n",
    "\n",
    "Now take the code above and create an MLRun function called `simple-graph`, of type `serving`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import mlrun\n",
    "\n",
    "project = mlrun.new_project(\n",
    "    \"basic-graph-demo\",\n",
    "    \"./\",\n",
    "    user_project=True,\n",
    "    init_git=True,\n",
    "    description=\"my new project\",\n",
    ")\n",
    "\n",
    "project.set_function(\n",
    "    name=\"simple-graph\", func=\"serving-graph.py\", kind=\"serving\", image=\"mlrun/mlrun\"\n",
    ")\n",
    "graph = fn.set_topology(\"flow\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Build the graph\n",
    "\n",
    "Use `graph.to()` to chain steps. Use `.respond()` to mark that the output of that step is returned to the caller\n",
    "(as an http response). By default the graph is async with no response."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<mlrun.serving.states.TaskStep at 0x7f821e504450>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.to(name=\"+1\", handler=\"inc\").to(name=\"*2\", handler=\"mul\").to(\n",
    "    name=\"(X+counter)\", class_name=\"WithState\"\n",
    ").respond()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize the graph\n",
    "\n",
    "Using the `plot` method, you can visualize the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.40.1 (20161225.0304)\n",
       " -->\n",
       "<!-- Title: mlrun&#45;flow Pages: 1 -->\n",
       "<svg width=\"431pt\" height=\"44pt\"\n",
       " viewBox=\"0.00 0.00 431.09 44.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 40)\">\n",
       "<title>mlrun&#45;flow</title>\n",
       "<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-40 427.0869,-40 427.0869,4 -4,4\"/>\n",
       "<!-- _start -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>_start</title>\n",
       "<polygon fill=\"#d3d3d3\" stroke=\"#000000\" points=\"38.5476,-.0493 40.698,-.1479 42.8263,-.2953 44.9236,-.4913 46.9815,-.7353 48.9917,-1.0266 50.9463,-1.3645 52.8377,-1.7479 54.6587,-2.1759 56.4025,-2.6472 58.0628,-3.1606 59.634,-3.7147 61.1107,-4.308 62.4882,-4.9388 63.7625,-5.6054 64.9302,-6.3059 65.9882,-7.0385 66.9343,-7.8012 67.7669,-8.5918 68.4849,-9.4082 69.0878,-10.2481 69.5758,-11.1093 69.9496,-11.9894 70.2102,-12.886 70.3595,-13.7965 70.3997,-14.7186 70.3334,-15.6497 70.1636,-16.5873 69.8937,-17.5287 69.5276,-18.4713 69.0691,-19.4127 68.5225,-20.3503 67.8923,-21.2814 67.1831,-22.2035 66.3996,-23.114 65.5464,-24.0106 64.6285,-24.8907 63.6504,-25.7519 62.617,-26.5918 61.5329,-27.4082 60.4024,-28.1988 59.2299,-28.9615 58.0197,-29.6941 56.7755,-30.3946 55.5012,-31.0612 54.2002,-31.692 52.8757,-32.2853 51.5309,-32.8394 50.1684,-33.3528 48.7908,-33.8241 47.4003,-34.2521 45.9989,-34.6355 44.5886,-34.9734 43.1708,-35.2647 41.7472,-35.5087 40.3189,-35.7047 38.8872,-35.8521 37.4531,-35.9507 36.0175,-36 34.5815,-36 33.146,-35.9507 31.7119,-35.8521 30.2801,-35.7047 28.8519,-35.5087 27.4282,-35.2647 26.0105,-34.9734 24.6001,-34.6355 23.1988,-34.2521 21.8083,-33.8241 20.4306,-33.3528 19.0681,-32.8394 17.7233,-32.2853 16.3989,-31.692 15.0979,-31.0612 13.8236,-30.3946 12.5794,-29.6941 11.3691,-28.9615 10.1967,-28.1988 9.0662,-27.4082 7.982,-26.5918 6.9486,-25.7519 5.9706,-24.8907 5.0526,-24.0106 4.1995,-23.114 3.4159,-22.2035 2.7067,-21.2814 2.0765,-20.3503 1.53,-19.4127 1.0715,-18.4713 .7053,-17.5287 .4355,-16.5873 .2657,-15.6497 .1993,-14.7186 .2395,-13.7965 .3888,-12.886 .6495,-11.9894 1.0232,-11.1093 1.5112,-10.2481 2.1141,-9.4082 2.8321,-8.5918 3.6647,-7.8012 4.6109,-7.0385 5.6689,-6.3059 6.8365,-5.6054 8.1108,-4.9388 9.4884,-4.308 10.9651,-3.7147 12.5362,-3.1606 14.1966,-2.6472 15.9404,-2.1759 17.7614,-1.7479 19.6528,-1.3645 21.6074,-1.0266 23.6176,-.7353 25.6755,-.4913 27.7728,-.2953 29.901,-.1479 32.0515,-.0493 34.2154,0 36.3837,0 38.5476,-.0493\"/>\n",
       "<text text-anchor=\"middle\" x=\"35.2995\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">start</text>\n",
       "</g>\n",
       "<!-- +1 -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>+1</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"133.5991\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"133.5991\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">+1</text>\n",
       "</g>\n",
       "<!-- _start&#45;&gt;+1 -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>_start&#45;&gt;+1</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M69.9563,-18C78.4445,-18 87.5661,-18 96.1775,-18\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"96.3518,-21.5001 106.3518,-18 96.3517,-14.5001 96.3518,-21.5001\"/>\n",
       "</g>\n",
       "<!-- *2 -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>*2</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"223.5991\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"223.5991\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">*2</text>\n",
       "</g>\n",
       "<!-- +1&#45;&gt;*2 -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>+1&#45;&gt;*2</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M160.602,-18C168.6267,-18 177.5656,-18 186.1299,-18\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"186.3041,-21.5001 196.3041,-18 186.304,-14.5001 186.3041,-21.5001\"/>\n",
       "</g>\n",
       "<!-- (X+counter) -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>(X+counter)</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"354.843\" cy=\"-18\" rx=\"68.4888\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"354.843\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">(X+counter)</text>\n",
       "</g>\n",
       "<!-- *2&#45;&gt;(X+counter) -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>*2&#45;&gt;(X+counter)</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M250.7929,-18C258.5123,-18 267.3271,-18 276.4861,-18\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"276.5242,-21.5001 286.5242,-18 276.5241,-14.5001 276.5242,-21.5001\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.dot.Digraph at 0x7f82294f2f90>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.plot(rankdir=\"LR\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test the function on a mock server\n",
    "\n",
    "Create a mock server and test the graph locally. Since this graph accepts a numeric value as the input, that value is provided \n",
    "in the `body` parameter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Echo: (X+counter), x: 12, counter: 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "server = fn.to_mock_server()\n",
    "server.test(body=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run the function again. This time, the counter should be 2 and the output should be 14."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Echo: (X+counter), x: 12, counter: 2\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "server.test(body=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploy the function\n",
    "\n",
    "Use the `deploy` method to deploy the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2021-11-08 07:30:21,571 [info] Starting remote function deploy\n",
      "2021-11-08 07:30:21  (info) Deploying function\n",
      "2021-11-08 07:30:21  (info) Building\n",
      "2021-11-08 07:30:21  (info) Staging files and preparing base images\n",
      "2021-11-08 07:30:21  (info) Building processor image\n",
      "2021-11-08 07:30:26  (info) Build complete\n",
      "2021-11-08 07:30:31  (info) Function deploy complete\n",
      "> 2021-11-08 07:30:31,785 [info] successfully deployed function: {'internal_invocation_urls': ['nuclio-basic-graph-demo-simple-graph.default-tenant.svc.cluster.local:8080'], 'external_invocation_urls': ['basic-graph-demo-simple-graph-basic-graph-demo.default-tenant.app.aganefaibuzg.iguazio-cd2.com/']}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'http://basic-graph-demo-simple-graph-basic-graph-demo.default-tenant.app.aganefaibuzg.iguazio-cd2.com/'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fn.deploy(project=\"basic-graph-demo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test the deployed function\n",
    "\n",
    "Use the `invoke` method to call the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2021-11-08 07:30:43,241 [info] invoking function: {'method': 'POST', 'path': 'http://nuclio-basic-graph-demo-simple-graph.default-tenant.svc.cluster.local:8080/'}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fn.invoke(\"\", body=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2021-11-08 07:30:48,359 [info] invoking function: {'method': 'POST', 'path': 'http://nuclio-basic-graph-demo-simple-graph.default-tenant.svc.cluster.local:8080/'}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fn.invoke(\"\", body=5)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
